package momo.multitree.simulation;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream.GetField;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import momo.multitree.structure.Graph;
import momo.multitree.structure.Tree;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class ParetoDominance {
	
private Log log = LogFactory.getLog(ParetoDominance.class);
	
	private int sizes[] = {50};
//	private String[] filenames = {"50_node_symmetric_dataset_39.txt",
//		"50_node_symmetric_dataset_40.txt",
//		"50_node_symmetric_dataset_95.txt",
//		"50_node_symmetric_dataset_96.txt",
//		"50_node_symmetric_dataset_92.txt",
//		};
	private String[] filenames;
	
	private Map<String, Double[]> treeValues;
	private Map<String, Double[]> weightValues;
	
	public ParetoDominance() {
		filenames = new String[ sizes.length*100 ];
		int counter = 0;
		for(int i=0; i<sizes.length; i++)
		{
			for(int j=0; j<100; j++) {
//				filenames[ j + (counter*100) ] = sizes[i]+"_node_symmetric_noleaf_dataset_" + (j+1) +".txt";
				filenames[ j + (counter*100) ] = sizes[i]+"_node_symmetric_dataset_" + (j+1) +".txt";
			}
			counter ++;
		}
		treeValues = new HashMap<String, Double[]>(); 
		weightValues = new HashMap<String, Double[]>();
		setupTree();
		setupWeight();
	}
	
	public void setupTree() {
		try {
			BufferedReader in = new BufferedReader(new FileReader(new File("result_optweight vs multi/Result_Tree_50.csv")));
			String line = "";
			while ( ( line=in.readLine() ) != null ) {
				String tokens[] = line.split(",");
				String key = tokens[0];
				key = key.substring(1, key.length()-1);
				Double[] values = { Double.valueOf(tokens[1]), 
									Double.valueOf(tokens[2]),
									Double.valueOf(tokens[3]),
									Double.valueOf(tokens[4]),
									Double.valueOf(tokens[5]),
									Double.valueOf(tokens[6]),
									Double.valueOf(tokens[7]),
									Double.valueOf(tokens[8]),
									Double.valueOf(tokens[9]),
									Double.valueOf(tokens[10]),
									Double.valueOf(tokens[11]),
									Double.valueOf(tokens[12])
									};
				treeValues.put(key, values);
			}
		}catch (FileNotFoundException e) {
			e.printStackTrace();
		}catch (IOException ioe) {
			ioe.printStackTrace();
		}
	}
	
	public void setupWeight() {
		try {
			BufferedReader in = new BufferedReader(new FileReader(new File("result_optweight vs multi/Result_Weight_50.csv")));
			String line = "";
			while ( ( line=in.readLine() ) != null ) {
				String tokens[] = line.split(",");
				String key = tokens[0];
				key = key.substring(1, key.length()-1);
				Double[] values = { Double.valueOf(tokens[1]), 
									Double.valueOf(tokens[2]),
									Double.valueOf(tokens[3]),
									Double.valueOf(tokens[4]),
									Double.valueOf(tokens[5]),
									Double.valueOf(tokens[6]),
									Double.valueOf(tokens[7]),
									Double.valueOf(tokens[8]),
									Double.valueOf(tokens[9]),
									Double.valueOf(tokens[10]),
									Double.valueOf(tokens[11]),
									Double.valueOf(tokens[12])
									};
				weightValues.put(key, values);
			}
		}catch (FileNotFoundException e) {
			e.printStackTrace();
		}catch (IOException ioe) {
			ioe.printStackTrace();
		}
	}
	
	public void run(int maxIteration_2, int maxTrial_2, double temperature_2, double factor_2) {
		int totalParetoDom = 0;
		int totalNewIsBetter = 0;
		int total = 0;
		
		double distA_1 = 0;
		double distB_1 = 0;
		double distA_2 = 0;
		double distB_2 = 0;
		
//		log.info("Max Iteration: " + maxIteration_2 + ", maxTrial: " + maxTrial_2 + ", temp: " + temperature_2 + ", factor: " + factor_2);
		log.info("Max Iteration: " + maxIteration_2);
		log.info("MaxTrial: " + maxTrial_2);
		log.info("Temp: " + temperature_2);
		log.info("Factor: " + factor_2);
		
		for(String filename: filenames) {
			try {
				Graph g = new Graph(new FileReader("dataset/"+filename));
								
				double optLatA_1 = this.getOpt( treeValues.get(filename)[0], treeValues.get(filename)[3], treeValues.get(filename)[6], treeValues.get(filename)[9] );
				double optCostA_1 = this.getOpt( treeValues.get(filename)[1], treeValues.get(filename)[4], treeValues.get(filename)[7], treeValues.get(filename)[10] );
				double optStabA_1 = this.getOpt( treeValues.get(filename)[2], treeValues.get(filename)[5], treeValues.get(filename)[8], treeValues.get(filename)[11] );
				double new_dist_A_1 = distToOpt(optLatA_1, optCostA_1, optStabA_1);
				distA_1 += new_dist_A_1;
								
				double optLatB_1 = this.getOpt( weightValues.get(filename)[0], weightValues.get(filename)[3], weightValues.get(filename)[6], weightValues.get(filename)[9] );
				double optCostB_1 = this.getOpt( weightValues.get(filename)[1], weightValues.get(filename)[4], weightValues.get(filename)[7], weightValues.get(filename)[10] );
				double optStabB_1 = this.getOpt( weightValues.get(filename)[2], weightValues.get(filename)[5], weightValues.get(filename)[8], weightValues.get(filename)[11] );
				double new_dist_B_1 = distToOpt(optLatB_1, optCostB_1, optStabB_1); 
				distB_1 += new_dist_B_1; 
				
//				int maxIteration_2 = 100;
//				int maxTrial_2 = 100;
//				double temperature_2 = 2d;
//				double factor_2 = 0.90d;
				
				SimulatedAnnealing simA_2 = new SimulatedAnnealing(g, maxIteration_2, maxTrial_2, temperature_2, factor_2);
				Tree bestTreeA_2 = simA_2.optimizeTree();
				Tree bestLatTreeA_2 = simA_2.getBestLatTree();
				Tree bestCostTreeA_2 = simA_2.getBestCostTree();
				Tree bestStabTreeA_2 = simA_2.getBestStabTree();
				
				double optLatA_2 = this.getOpt( bestLatTreeA_2.compWeightedLatency(), bestCostTreeA_2.compWeightedLatency(), bestStabTreeA_2.compWeightedLatency(), bestTreeA_2.compWeightedLatency() );
				double optCostA_2 = this.getOpt( bestLatTreeA_2.compCost(false), bestCostTreeA_2.compCost(false), bestStabTreeA_2.compCost(false), bestTreeA_2.compCost(false) );
				double optStabA_2 = this.getOpt( bestLatTreeA_2.compStability(), bestCostTreeA_2.compStability(), bestStabTreeA_2.compStability(), bestTreeA_2.compStability() );
				double new_dist_A_2 = distToOpt(optLatA_2, optCostA_2, optStabA_2); 
				distA_2 += new_dist_A_2;
				
				OptWeightedTree simB_2 = new OptWeightedTree(g, maxIteration_2, maxTrial_2, temperature_2, factor_2);
				Tree bestTreeB_2 = simB_2.optimizeTree();
				Tree bestLatTreeB_2 = simB_2.getBestLatTree();
				Tree bestCostTreeB_2 = simB_2.getBestCostTree();
				Tree bestStabTreeB_2 = simB_2.getBestStabTree();
				
				double optLatB_2 = this.getOpt( bestLatTreeB_2.compWeightedLatency(), bestCostTreeB_2.compWeightedLatency(), bestStabTreeB_2.compWeightedLatency(), bestTreeB_2.compWeightedLatency() );
				double optCostB_2 = this.getOpt( bestLatTreeB_2.compCost(false), bestCostTreeB_2.compCost(false), bestStabTreeB_2.compCost(false), bestTreeB_2.compCost(false) );
				double optStabB_2 = this.getOpt( bestLatTreeB_2.compStability(), bestCostTreeB_2.compStability(), bestStabTreeB_2.compStability(), bestTreeB_2.compStability() );
				double new_dist_B_2 = distToOpt(optLatB_2, optCostB_2, optStabB_2); 
				distB_2 += new_dist_B_2;
				
				log.info(filename);
				
				int paretoDom = this.compareParetoDominance(optLatA_2, optCostA_2, optStabA_2, optLatB_2, optCostB_2, optStabB_2);
				totalParetoDom += paretoDom;
				if ( paretoDom > 0 )
					log.info(filename + ", pareto dominance" );
				
//				log.info(filename+", " + new_dist_A_1 + ", " + new_dist_A_2 +", " + new_dist_B_1 + ", " + new_dist_B_2);
				int newIsBetter = ( new_dist_A_2 < new_dist_A_1 ? 1 : 0 );
				totalNewIsBetter += newIsBetter;
				if ( newIsBetter == 0 )
					log.info(filename + ", old is better" );
				
				total += 1;
				
			}catch(Exception e)
			{
				log.error("Error on filename: " + filename);
				e.printStackTrace();
			}
		}
		log.info("Total Pareto Dominance: " + totalParetoDom );
		log.info("Dist_A_1: " + distA_1 / total );
		log.info("Dist_A_2: " + distA_2 / total );
		log.info("Dist_B_1: " + distB_1 / total );
		log.info("Dist_B_2: " + distB_2 / total );
		log.info("Dist_A_2 is better in #" + totalNewIsBetter );
	}
	
	public String displayInfo(Tree bestTree)
	{
		return bestTree.compWeightedLatency() + ", " +
				bestTree.compCost(false) + ", " +
				bestTree.compStability();
	}
	
	public double getOpt(double bestLatTree, double bestCostTree, double bestStabTree, double bestTree) {
		double max = max( bestLatTree, bestCostTree, bestStabTree );
		double min = min( bestLatTree, bestCostTree, bestStabTree );
		double range = max - min;
		double opt = range == 0 ? 0 :  ( bestTree - min ) / range;
		return opt;
	}
	
	public int compareParetoDominance(
			double optLatA, double optCostA, double optStabA,
			double optLatB, double optCostB, double optStabB) {
		return (optLatB < optLatA) && (optCostB < optCostA) && (optStabB < optStabA) == true ? 1:0;
	}
	
	public double distToOpt(double valA, double valB, double valC) {
		return Math.sqrt( valA*valA + valB*valB + valC*valC );
	}
	
	public double min(double valA, double valB, double valC) {
		return Math.min( Math.min(valA, valB), valC );
	}
	
	public double max(double valA, double valB, double valC) {
		return Math.max( Math.max(valA, valB), valC );
	}
	
	private int getPositiveIntValue(String raw) {
		int value = -1;
		try {
			value = Integer.valueOf(raw);
		}catch (Exception e) {
		}
		return value;
	}
	
	private double getPositiveDoubleValue(String raw) {
		double value = -1.0d;
		try {
			value = Double.valueOf(raw);
		}catch (Exception e) {
		}
		return value;
	}
	
	public static void main(String args[]) {
		
		int maxIteration = 100;
		int maxTrial = 100;
		double temperature = 1d;
		double factor = 0.95d;
		
		ParetoDominance app = new ParetoDominance();
		
		for(int i=0; i<args.length; i++) {
			String raw = args[i];
			if ( raw.substring(0, 1).equalsIgnoreCase("i") ) {
				int v = app.getPositiveIntValue(raw.substring(1));
				maxIteration = v>=0?v:maxIteration;
			}else if ( raw.substring(0, 1).equalsIgnoreCase("r") ) {
				int v = app.getPositiveIntValue(raw.substring(1));
				maxTrial = v>=0?v:maxTrial;
			}else if ( raw.substring(0, 1).equalsIgnoreCase("t") ) {
				double v = app.getPositiveDoubleValue(raw.substring(1)) ;
				temperature = v>=0?v:temperature;
			}else if ( raw.substring(0, 1).equalsIgnoreCase("f") ) {
				double v = app.getPositiveDoubleValue(raw.substring(1)) ;
				factor = v>=0?v:factor;
			}
		}
		
		app.run(maxIteration, maxTrial, temperature, factor);
	}
	
} //end of class ParetoDominance
